package eip.chapter7.contentrouter.mule;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.mule.config.i18n.Message;
import org.mule.config.i18n.Messages;
import org.mule.impl.endpoint.MuleEndpointURI;
import org.mule.routing.outbound.AbstractOutboundRouter;
import org.mule.umo.UMOException;
import org.mule.umo.UMOMessage;
import org.mule.umo.UMOSession;
import org.mule.umo.endpoint.EndpointException;
import org.mule.umo.endpoint.UMOEndpoint;
import org.mule.umo.endpoint.UMOEndpointURI;
import org.mule.umo.routing.CouldNotRouteOutboundMessageException;
import org.mule.umo.routing.RoutePathNotFoundException;
import org.mule.umo.routing.RoutingException;
import org.mule.util.TemplateParser;

import eip.chapter7.contentrouter.HotelBooking;
import eip.chapter7.contentrouter.util.JiBXHelper;

public class HotelBookingRouter extends AbstractOutboundRouter {
	
	// We used Square templates as they can exist as part of an uri.
    private TemplateParser parser = TemplateParser.createSquareBracesStyleParser();
	
    public UMOMessage route(UMOMessage message, UMOSession session, boolean synchronous) throws RoutingException {
        UMOMessage result = null;

        if (endpoints == null || endpoints.size() == 0) {
            throw new RoutePathNotFoundException(new Message(Messages.NO_ENDPOINTS_FOR_ROUTER), message, null);
        }

        UMOEndpoint ep = getEndpoint(message);
        if(logger.isInfoEnabled()) {
        	logger.info("routing message to " + ep.getEndpointURI());
        }
        try {
            if (synchronous) {
                result = send(session, message, ep);
            } else {
                dispatch(session, message, ep);
            }
        } catch (UMOException e) {
            throw new CouldNotRouteOutboundMessageException(message, ep, e);
        }
        return result;
    }
    
    public boolean isMatch(UMOMessage message) throws RoutingException {
        return true;
    }

    /**
     * Will Return the endpont at the given index and will resolve any template tags on the Endpoint URI if necessary
     * @param index the index of the endpoint to get
     * @param message the current message.  This is required if template matching is being used
     * @return the endpoint at the index, with any template tags resolved
     * @throws CouldNotRouteOutboundMessageException if the template causs the endpoint to become illegal or malformed
     */
    @SuppressWarnings("unchecked")
	public UMOEndpoint getEndpoint(UMOMessage message) throws CouldNotRouteOutboundMessageException {
        
    	UMOEndpoint destEndpoint = null;
    	try {
    		HotelBooking booking = JiBXHelper.createBookingFromXML(message.getPayloadAsString());
    		if(logger.isInfoEnabled()) {
    			logger.info("received hotel booking in router " + booking);
    		}
	    	Iterator itEndpoint = endpoints.iterator();
	    	while(itEndpoint.hasNext()) {
	    		UMOEndpoint ep = (UMOEndpoint) itEndpoint.next();
	    		if(ep.getName().equals(booking.getHotelCity())) {
	    			destEndpoint = ep;
	    		}
	    	}
    	} catch(Exception e) {
    		throw new CouldNotRouteOutboundMessageException(new Message(Messages.MESSAGE_X_NOT_SUPPORTED_BY_ADAPTER_X,
                    null, null), message, destEndpoint, e);
    	}
        if(destEndpoint == null) {
        	destEndpoint = (UMOEndpoint) endpoints.get(endpoints.size() - 1);
        }
        String uri = destEndpoint.getEndpointURI().toString();
        if(logger.isDebugEnabled()) {
            logger.debug("Uri before parsing is: " + uri);
        }
        Map props = new HashMap();
        for (Iterator iterator = message.getPropertyNames().iterator(); iterator.hasNext();) {
            String propertyKey = (String)iterator.next();
            props.put(propertyKey, message.getProperty(propertyKey));
        }
        String newUriString = parser.parse(props, uri);
        if(logger.isDebugEnabled()) {
            logger.debug("Uri after parsing is: " + uri);
        }
        try {
            UMOEndpointURI newUri = new MuleEndpointURI(newUriString);
            if(!newUri.getScheme().equalsIgnoreCase(destEndpoint.getEndpointURI().getScheme())) {
                throw new CouldNotRouteOutboundMessageException(new Message(Messages.SCHEME_CANT_CHANGE_FOR_ROUTER_X_X,
                		destEndpoint.getEndpointURI().getScheme(), newUri.getScheme()), message, destEndpoint);
            }
            destEndpoint.setEndpointURI(newUri);
        } catch (EndpointException e) {
            throw new CouldNotRouteOutboundMessageException(new Message(Messages.TEMPLATE_X_CAUSED_MALFORMED_ENDPOINT_X,
                        uri, newUriString), message, destEndpoint, e);
        }

        return destEndpoint;
    }
}
